package cn.b504.qpcr.impls.cal;

import cn.b504.qpcr.entity.Data;
import cn.b504.qpcr.entity.PlateSetting;
import cn.b504.qpcr.entity.Well;
import cn.b504.qpcr.interfaces.cal.CalMeanAndSd;
import cn.b504.qpcr.utils.CalCqUtils;
import cn.b504.qpcr.utils.Num;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author ssc
 */
@Service
public class CalMeanAndSdImpl implements CalMeanAndSd {

  @Override
  public Future<?> cq(PlateSetting.Target target, Data data) {
    final short channelIndex = target.getChannelIndex();
    // 重置 CqMean 和 CqSd
    data.getWells().forEach(well -> {
      well.getLines()[channelIndex].setCqMean(-1);
      well.getLines()[channelIndex].setCqSd(-1);
    });
    // 取出符合 target name 一致的 well 孔
    Stream<Well> wellStream = data.getWells().stream().filter(it -> !CalCqUtils.shouldSkip(it, target.getName(), channelIndex));
    List<Well> wellList = wellStream.collect(Collectors.toList());
    final Set<String> set = new HashSet<>();
    // 遍历每一个孔
    wellList.forEach(it -> {
      final String sampleName = it.getSampleName();
      if (set.contains(sampleName)) {
        return;
      }
      set.add(sampleName);

      Num num = new Num();
      // 将所有 sample name 一致的 cq 作累加
      wellList.forEach(well -> {
        if (!sampleName.equals(well.getSampleName())) {
          return;
        }
        float cq = well.getLines()[channelIndex].getCq();
        if (cq == -1) {
          return;
        }
        num.q += cq;
        num.n++;
      });
      if (num.n == 0) {
        return;
      }
      // 对所有 sample name 一致的 cqMean 赋值。同时为计算 cqSd 做准备
      float cqMean = (float) (num.q / num.n);
      wellList.forEach(well -> {
        if (!sampleName.equals(well.getSampleName())) {
          return;
        }
        float cq = well.getLines()[channelIndex].getCq();
        if (cq == -1) {
          return;
        }
        well.getLines()[channelIndex].setCqMean(cqMean);
        num.s += Math.pow(cq - cqMean, 2);
      });
      // 对所有 sample name 一致的 cqSd 赋值。
      float cqSd = (float) Math.sqrt(num.s / num.n);
      wellList.forEach(well -> {
        if (!sampleName.equals(well.getSampleName())) {
          return;
        }
        if (well.getLines()[channelIndex].getCq() == -1) {
          return;
        }
        well.getLines()[channelIndex].setCqSd(cqSd);
      });
    });
    return new AsyncResult<>(null);
  }

  @Override
  public Future<?> sq(PlateSetting.Target target, Data data) {
    final short channelIndex = target.getChannelIndex();
    // 重置 SqMean 和 SqSd
    data.getWells().forEach(well -> {
      well.getLines()[channelIndex].setSqMean(-1);
      well.getLines()[channelIndex].setSqSd(-1);
    });
    // 取出符合 target name 一致的 well 孔
    Stream<Well> wellStream = data.getWells().stream().filter(it -> !CalCqUtils.shouldSkip(it, target.getName(), channelIndex));
    List<Well> wellList = wellStream.collect(Collectors.toList());
    final Set<String> set = new HashSet<>();
    // 遍历每一个孔
    wellList.forEach(it -> {
      final String sampleName = it.getSampleName();
      if (set.contains(sampleName)) {
        return;
      }
      set.add(sampleName);

      Num num = new Num();
      // 将所有 sample name 一致的 sq 作累加
      wellList.forEach(well -> {
        if (!sampleName.equals(well.getSampleName())) {
          return;
        }
        float sq = well.getLines()[channelIndex].getSq();
        if (sq == -1) {
          return;
        }
        num.q += sq;
        num.n++;
      });
      if (num.n == 0) {
        return;
      }
      // 对所有 sample name 一致的 sqMean 赋值。同时为计算 sqSd 做准备
      float sqMean = (float) (num.q / num.n);
      wellList.forEach(well -> {
        if (!sampleName.equals(well.getSampleName())) {
          return;
        }
        float sq = well.getLines()[channelIndex].getSq();
        if (sq == -1) {
          return;
        }
        well.getLines()[channelIndex].setSqMean(sqMean);
        num.s += Math.pow(sq - sqMean, 2);
      });
      // 对所有 sample name 一致的 sqSd 赋值。
      float sqSd = (float) Math.sqrt(num.s / num.n);
      wellList.forEach(well -> {
        if (!sampleName.equals(well.getSampleName())) {
          return;
        }
        float sq = well.getLines()[channelIndex].getSq();
        if (sq == -1) {
          return;
        }
        well.getLines()[channelIndex].setSqSd(sqSd);
      });
    });
    return new AsyncResult<>(null);
  }
}
